/*
* Copyright (c) 2005, Nikolay Chetvertinovsky
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the copyright holder nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// xmlutils.cpp
//
////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "xmlutils.h"

////////////////////////////////////////////////////////////////////////////////

BOOL LoadXMLDocument(const CString& sData, BOOL bFile, MSXML::IXMLDOMDocumentPtr& pDoc, CString& sError)
{
  BOOL br = FALSE;
  MSXML::IXMLDOMParseErrorPtr pParsingErr = NULL;

  if ( FAILED(CoCreateInstance( MSXML::CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, MSXML::IID_IXMLDOMDocument, (LPVOID*)&pDoc )) ) {
    sError = _T("Can't create the MSXML Document");
    return FALSE;
  }

  VARIANT_BOOL vbr;
  CString sDescription;
  pDoc->put_async( VARIANT_FALSE );
  if ( bFile )
    vbr = pDoc->load( _variant_t((BSTR)_bstr_t(sData)) );
  else
    vbr = pDoc->loadXML( _bstr_t(sData) );

  if ( vbr ) {
    br = TRUE;
  } else {
    pParsingErr = pDoc->GetparseError();

    LONG lError;
    pParsingErr->get_errorCode( &lError );

    if ( lError == -2146697210 )
      sDescription = _T("File not found. The file could not be found at the specified URL");
    else {
      LONG lLine = pParsingErr->Getline();
      LONG lLinePos = pParsingErr->Getlinepos();
      _bstr_t sReason = pParsingErr->Getreason();
      sDescription.Format( _T("Line: %d, Pos: %d, Description: %s"), lLine, lLinePos, (LPCTSTR)sReason );
    }
    br = FALSE;
  }
  if ( !br ) {
    if ( bFile )
      sError.Format( _T("Loading file \'%s\' error.\r\n%s"), sData, sDescription );
    else
      sError.Format( _T("Loading xml \'%s\' error.\r\n%s"), sData, sDescription );
  }

  return br;
}

HRESULT GetNodeText(MSXML::IXMLDOMNodePtr& pParent, const _bstr_t& name, CString& sText)
{
  HRESULT hr;
  _bstr_t bsText;
  MSXML::IXMLDOMNodePtr pNode = NULL;

  hr = GetNode( pParent, name, pNode );
  if ( SUCCEEDED(hr) ) {
    hr = pNode->get_text( &bsText.GetBSTR() );
    if ( SUCCEEDED(hr) ) {
      sText = (LPCTSTR)bsText;
    }
  }

  return hr;
}

HRESULT GetNode(MSXML::IXMLDOMNodePtr& pParent, const _bstr_t& name, MSXML::IXMLDOMNodePtr& pNode)
{
  HRESULT hr;
  if ( pParent == NULL || name.length() == 0 )
    return E_INVALIDARG;

  MSXML::DOMNodeType eType = (MSXML::DOMNodeType)0; //MSXML::NODE_INVALID;
  hr = pParent->get_nodeType( &eType );
  ATLASSERT( SUCCEEDED(hr) && eType == MSXML::NODE_ELEMENT );

  VARIANT_BOOL bChilds;
  hr = pParent->raw_hasChildNodes( &bChilds );
  if ( FAILED(hr) )
    return hr;

  if ( bChilds == VARIANT_FALSE )
    return E_FAIL;

  MSXML::IXMLDOMNodeListPtr childs = NULL;
  hr = pParent->get_childNodes( &childs );
  if ( FAILED(hr) )
    return hr;

  long nLen;
  hr = childs->get_length( &nLen );
  if ( FAILED(hr) )
    return hr;

  for ( int i = 0; i < nLen; i++ ) {
    MSXML::IXMLDOMNodePtr childnode = NULL;
    hr = childs->get_item( i, &childnode );
    if ( FAILED(hr) )
      return hr;

    BSTR bsName;
    hr = childnode->get_nodeName( &bsName );
    if ( FAILED(hr) )
      return hr;

    _bstr_t sName(bsName, FALSE);
    if ( !_wcsicmp( sName, name ) ) {
      pNode = childnode;
      return S_OK;
    }
  }

  return E_FAIL;
}

HRESULT GetStrAttribute(MSXML::IXMLDOMNodePtr& pParent, const _bstr_t& name, CString& str)
{
  HRESULT hr;
  _bstr_t sVal;
  _variant_t vVal;

  hr = GetAttribute( pParent, name, vVal );
  if ( SUCCEEDED(hr) ) {
    vVal.ChangeType( VT_BSTR ); sVal = vVal;
    str = (LPCTSTR)sVal;
  }
  return hr;
}

HRESULT GetAttribute(MSXML::IXMLDOMNodePtr& pParent, const _bstr_t& name, _variant_t& val)
{
  HRESULT hr;
  if ( pParent == NULL || name.length() == 0 )
    return E_INVALIDARG;

  MSXML::DOMNodeType eType = (MSXML::DOMNodeType)0; //MSXML::NODE_INVALID;
  hr = pParent->get_nodeType( &eType );
  ATLASSERT( SUCCEEDED(hr) && eType == MSXML::NODE_ELEMENT );

  MSXML::IXMLDOMNamedNodeMapPtr spAttrs = NULL;
  hr = pParent->get_attributes( &spAttrs );
  if ( FAILED(hr) )
    return FALSE;

  MSXML::IXMLDOMNodePtr spChild = NULL;
  hr = spAttrs->raw_nextNode( &spChild );
  while ( SUCCEEDED(hr) && spChild != NULL ) {
    BSTR bsName;
    hr = spChild->get_nodeName( &bsName );
    if ( FAILED(hr) )
      return hr;

    _bstr_t sName(bsName, FALSE);
    if ( !_wcsicmp( sName, name ) ) {
      return spChild->get_nodeValue( &val );
    }

    spChild = NULL;
    hr = spAttrs->raw_nextNode( &spChild );
  }

  return E_FAIL;
}

HRESULT AddText(MSXML::IXMLDOMDocumentPtr& pDoc, MSXML::IXMLDOMNodePtr& pNode, const _bstr_t& text)
{
  HRESULT hr;
  if ( pDoc == NULL || pNode == NULL || text.length() == 0 )
    return E_INVALIDARG;

  MSXML::IXMLDOMTextPtr pText         = NULL;
  hr = pDoc->raw_createTextNode( text, &pText );
  if ( FAILED(hr) )
    return hr;

  hr = pNode->raw_appendChild( pText, NULL );
  if ( FAILED(hr) )
    return hr;

  return S_OK;
}

HRESULT AddNode(MSXML::IXMLDOMDocumentPtr& pDoc, MSXML::IXMLDOMNodePtr& pParent, const _bstr_t& name, MSXML::IXMLDOMNodePtr& pNode)
{
  HRESULT hr;
  if ( pDoc == NULL || pNode != NULL || name.length() == 0 )
    return E_INVALIDARG;

  hr = pDoc->raw_createNode( _variant_t((LONG)MSXML::NODE_ELEMENT, VT_I4), name, _bstr_t(_T("")), &pNode );
  if ( FAILED(hr) )
    return hr;

  if ( pParent != NULL )
    return pParent->raw_appendChild( pNode, NULL );

  return S_OK;
}

HRESULT AddNode2(MSXML::IXMLDOMDocumentPtr& pDoc, MSXML::IXMLDOMNodePtr& pParent, const _bstr_t& name, int nLevel, MSXML::IXMLDOMNodePtr& pNode)
{
  HRESULT hr;
  if ( pDoc == NULL || pNode != NULL || name.length() == 0 || nLevel <= 0 )
    return E_INVALIDARG;

  int nBefore = nLevel;
  int nPost   = nLevel-1;
  LPCWSTR pwzSpace = L"  ";
  LPCWSTR pwzNewLine = L"\r\n";
  _bstr_t bsPost(pwzNewLine); while ( nPost-- ) bsPost += pwzSpace;

  hr = pDoc->raw_createNode( _variant_t((LONG)MSXML::NODE_ELEMENT, VT_I4), name, _bstr_t(L""), &pNode );
  if ( FAILED(hr) )
    return hr;

  if ( pParent != NULL ) {
    MSXML::IXMLDOMNodeListPtr childs = NULL;

    long nLen;
    hr = pParent->get_childNodes( &childs );
    hr = childs->get_length( &nLen );

    _bstr_t bsBefore;
    if ( nLen == 0 )
      bsBefore = pwzNewLine;
    else
      nBefore = 1;
    while ( nBefore-- ) bsBefore += pwzSpace;
    if ( bsBefore.length() )
      AddText( pDoc, pParent, bsBefore );

    hr = pParent->raw_appendChild( pNode, NULL );
    AddText( pDoc, pParent, bsPost );
  }

  return S_OK;
}

HRESULT AddAttribute(MSXML::IXMLDOMDocumentPtr& pDoc, MSXML::IXMLDOMNodePtr& pParent, const _bstr_t& name, _variant_t& val)
{
  HRESULT hr;
  if ( pDoc == NULL || pParent == NULL || name.length() == 0 )
    return E_INVALIDARG;

  MSXML::IXMLDOMAttributePtr pAttr = NULL;
  hr = pDoc->raw_createAttribute( name, &pAttr );
  if ( FAILED(hr) )
    return hr;

  hr = pAttr->put_value( val );
  if ( FAILED(hr) )
    return hr;

  MSXML::IXMLDOMNamedNodeMapPtr pAttrs = NULL;
  hr = pParent->get_attributes( &pAttrs );
  if ( FAILED(hr) )
    return hr;

  return pAttrs->raw_setNamedItem( pAttr, NULL );
}

////////////////////////////////////////////////////////////////////////////////
